//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Classe para tirar media de conjuto de dados
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

#include <cstdlib>
#include <vector>
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include "lib_zabot/aborta.h"

#define HISTLIB

using namespace std; 

class Histograma {

  vector<vector<double> > hist;
  vector<double> config;
  double mmax;
  double mmin;
  double m;
  double ss2;
  int type; // type = 0 uniforme
            // type = 1 nao uniforme
  //  const vector<double> hist_defaut = vector<double>();

  void ajusta_config(const vector<double> &format);
  void ajusta_hist(const vector<double> &format);
  void mm(void);
  void s2(void);

public:
  Histograma();
  Histograma(const vector<double> &dados,const vector<double> &format);
  Histograma(const vector<vector<double> > &dados);
  Histograma(const vector<double> &grid);
  //  Histograma(const Histograma &cp_hist); //copia um histograma pronto
  Histograma(const vector<vector<double> > &dados,const vector<double> &format, double len);
  ~Histograma()
  {
#ifdef DEBUG_HIST
    cout << "# Histograma destructor called...\n";
#endif
    hist.clear();
    config.clear();
  };
  void imprime(ostream& out);
  void new_data(const vector<double> &dados,const vector<double> &format);
  double inline le1(const int n){return hist[0][n];};
  double inline le2(const int n){return hist[1][n];};	
  const vector<double>& col1(void){return hist[0];};
  const vector<double>& col2(void){return hist[1];};
  void load_data(const vector<vector<double> > &dados, const vector<int> &pixmap, double len);
  void inline load_hist(const vector<vector<double> > &dados)
    {
      if((dados.size() != 2) || (dados[0].size() != dados[1].size())) {
	cout << "ERRO: Input de formato errado!\n";
	exit(0);
      }
      
      type = 1;
      
      //acho o menor e maior valor
      typedef vector<double>::const_iterator it;
      hist.resize(2);
      max(dados[0][0]);
      min(dados[0][0]);
      it iter1 = dados[1].begin();
      for( it iter0 = dados[0].begin(); iter0 != dados[0].end(); iter0++){
	if ( max() < *iter0 ) max(*iter0);
	if ( min() > *iter0 ) min(*iter0);
	hist[0].push_back(*iter0);
	if(iter1 != dados[1].end()) hist[1].push_back(*iter1);
	else {
	  cout << "ERRO: Input em tamanhos diferentes...\n";
	  exit(0);
	  
	  iter1++;
	}
      }
      mm();
      s2();
    };

  int inline PixPos(const double val)
    {
      if(type == 0){
	int ppos = static_cast<int>( floor( 0.5+(val-mmin)/config[2] ) );
	if (ppos == -1) ppos=0;
	return ppos;
      }
      if(type == 1){
	// Caso de histograma nao uniforme! Considero que cada intervalo comessa na metade
	// inferior e termina na metade superior. O ultimo valor possui tamanho uniforme 
	// i.e. considero que o intervalo inferior é igual ao intervalo superior.
	typedef vector<double>::const_iterator it;
	int index=0;
	for( it iter0 = hist[0].begin(); iter0 != hist[0].end(); iter0++, index++){
	  //Calculo limites superior e inferior
	  double step_up = ((iter0+1) != hist[0].end()) ? (( *(iter0+1) - *iter0 ) / 2.0)  : (( *iter0 - *(iter0-1) ) / 2.0);
	  double step_dn = (iter0 == hist[0].begin()) ? (( *(iter0+1) - *iter0 ) / 2.0)  : (( *iter0 - *(iter0-1) ) / 2.0);
	  
	  if(*iter0 - step_dn < val && val < *iter0 + step_up)
	    return index;
	    
	}

	return -1; // Nao encontrado...

      }
	
      return -1; // Nao encontrado...
      
    };
	int inline size(void)
	{
		return hist[0].size();
	};
	
  double media(bool);
  double sig2(bool);
  void inline max(double smax){mmax=smax;};
  void inline min(double smin){mmin=smin;};
  double inline max(void){return mmax;};
  double inline min(void){return mmin;};
  double inline step_med(void){return config[2];}

  double inline step_up(int n)
    {
      if((n > hist[0].size()) || n < 0) throw out_of_range("# - Posicao fora do histograma");

      if(type == 0) return config[2];
      if(type == 1) {
	vector<double>::const_iterator iter0 = hist[0].begin();
	iter0+=n;
	return ((iter0+1) != hist[0].end()) ? (( *(iter0+1) - *iter0 ) / 2.0) : (( *iter0 - *(iter0-1) ) / 2.0);
      }
		return -1;
    };

  double inline step_down(int n)
    {
      if((n > hist[0].size()) || n < 0) throw out_of_range("# - Posicao fora do histograma");

      if(type == 0) return config[2];
      if(type == 1) {
	vector<double>::const_iterator iter0 = hist[0].begin();
	iter0+=n;
	return (iter0 == hist[0].begin()) ? (( *(iter0+1) - *iter0 ) / 2.0)  : (( *iter0 - *(iter0-1) ) / 2.0);
      }
		return -1;
    };


  double inline step(int n)
    {
      if((n > hist[0].size()) || n < 0) throw out_of_range("# - Posicao fora do histograma");

      if(type == 0) return config[2];
      if(type == 1) {
	vector<double>::const_iterator iter0 = hist[0].begin();
	iter0+=n;
	double step_up = (iter0 != hist[0].end()) ? (( *(iter0+1) - *iter0 ) / 2.0)  : (( *iter0 - *(iter0-1) ) / 2.0);
	double step_dn = (iter0 == hist[0].begin()) ? (( *(iter0+1) - *iter0 ) / 2.0)  : (( *iter0 - *(iter0-1) ) / 2.0);
	
	return (step_up+step_dn)/2.0;
      }
      
      cout << "# - Tipo de histograma nao especificado\n# - return -1;";

      return -1; // type nao especificado!?
    }

  Histograma operator*= (double a){
	for(int i = 0; i < size(); i++)
	  hist[1][i] *= a;
	return *this;
      
  }
	
};

ostream& operator<< (ostream&, Histograma);


